using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Rocuna.Graphics.Interfaces;

namespace Rocuna.Graphics.Core
{
    /// <summary>
    /// Base class for all the cameras
    /// </summary>
    public abstract class Camera : GameComponent, ICameraService
    {
        #region Constructor

        protected Camera(Game game)
            : base(game)
        {
            GeneratePerspectiveProjectionMatrix(0f);
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the view matrix.
        /// </summary>
        /// <value>
        /// The view matrix.
        /// </value>
        public Matrix View
        {
            get { return _view; }
            protected set
            {
                _view = value;
                GenerateFrustom();
            }
        }

        /// <summary>
        /// Gets the projection near.
        /// </summary>
        public float ProjectionNear { get { return _nearPlaneDistance; } }

        /// <summary>
        /// Gets the projection top.
        /// </summary>
        public float ProjectionTop { get { return 0.5f; } }

        /// <summary>
        /// Gets or sets the projection matrix.
        /// </summary>
        /// <value>
        /// The projection matrix.
        /// </value>
        public Matrix Projection
        {
            get { return _projection; }
            protected set
            {
                _projection = value;
                GenerateFrustom();
            }
        }

        /// <summary>
        /// Gets or sets the position.
        /// </summary>
        /// <value>
        /// The position.
        /// </value>
        public Vector3 Position { get; set; }

        /// <summary>
        /// Gets or sets the target.
        /// </summary>
        /// <value>
        /// The target.
        /// </value>
        public Vector3 Target { get; set; }

        /// <summary>
        /// Gets the frustum.
        /// </summary>
        public BoundingFrustum Frustum { get; private set; }

        /// <summary>
        /// Gets the name.
        /// </summary>
        public abstract string Name { get; }

        /// <summary>
        /// Gets or sets the near plane distance.
        /// </summary>
        /// <value>
        /// The near plane distance.
        /// </value>
        public float NearPlaneDistance
        {
            get { return _nearPlaneDistance; }
            set { _nearPlaneDistance = value; }
        }

        /// <summary>
        /// Gets or sets the far plane distance.
        /// </summary>
        /// <value>
        /// The far plane distance.
        /// </value>
        public float FarPlaneDistance
        {
            get { return _farPlaneDistance; }
            set { _farPlaneDistance = value; }
        }

        #endregion

        #region Fields

        private Matrix _view;

        private Matrix _projection;

        private float _nearPlaneDistance = 0.1f;

        private float _farPlaneDistance = 10000f;

        #endregion

        #region Methods

        private void GeneratePerspectiveProjectionMatrix(float fieldOfView)
        {
            PresentationParameters parameters = Game.GraphicsDevice.PresentationParameters;

            float aspectRatio = (float)parameters.BackBufferWidth / (float)parameters.BackBufferHeight;

            Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45), aspectRatio, NearPlaneDistance, FarPlaneDistance);
        }

        /// <summary>
        /// Activates this instance.
        /// </summary>
        public virtual void Activate()
        {
            Game.Components.Add(this);
        }

        /// <summary>
        /// Deactivates this instance.
        /// </summary>
        public virtual void Deactivate()
        {
            Game.Components.Remove(this);
        }

        /// <summary>
        /// Boundings the volume is in view.
        /// </summary>
        /// <param name="sphere">The sphere.</param>
        /// <returns></returns>
        public bool BoundingVolumeIsInView(BoundingSphere sphere)
        {
            return (Frustum.Contains(sphere) != ContainmentType.Disjoint);
        }

        /// <summary>
        /// Boundings the volume is in view.
        /// </summary>
        /// <param name="box">The box.</param>
        /// <returns></returns>
        public bool BoundingVolumeIsInView(BoundingBox box)
        {
            return (Frustum.Contains(box) != ContainmentType.Disjoint);
        }

        #endregion

        #region Private methods

        private void GenerateFrustom()
        {
            var viewProjection = View * Projection;
            Frustum = new BoundingFrustum(viewProjection);
        }

        #endregion
    }
}